<!DOCTYPE HTML PUBLIC "-//ORA//DTD CD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE>[Chapter 11] 11.5 Localizing User-Visible Messages</TITLE>
<META NAME="author" CONTENT="David Flanagan">
<META NAME="date" CONTENT="Thu Jul 31 15:59:10 1997">
<META NAME="form" CONTENT="html">
<META NAME="metadata" CONTENT="dublincore.0.1">
<META NAME="objecttype" CONTENT="book part">
<META NAME="otheragent" CONTENT="gmat dbtohtml">
<META NAME="publisher" CONTENT="O'Reilly &amp; Associates, Inc.">
<META NAME="source" CONTENT="SGML">
<META NAME="subject" CONTENT="Java">
<META NAME="title" CONTENT="Java in a Nutshell">
<META HTTP-EQUIV="Content-Script-Type" CONTENT="text/javascript">
</HEAD>
<body vlink="#551a8b" alink="#ff0000" text="#000000" bgcolor="#FFFFFF" link="#0000ee">

<DIV CLASS=htmlnav>
<H1><a href='index.htm'><IMG SRC="gifs/smbanner.gif"
     ALT="Java in a Nutshell" border=0></a></H1>
<table width=515 border=0 cellpadding=0 cellspacing=0>
<tr>
<td width=172 align=left valign=top><A HREF="ch11_04.htm"><IMG SRC="gifs/txtpreva.gif" ALT="Previous" border=0></A></td>
<td width=171 align=center valign=top><B><FONT FACE="ARIEL,HELVETICA,HELV,SANSERIF" SIZE="-1">Chapter 11<br>Internationalization</FONT></B></TD>
<td width=172 align=right valign=top><A HREF="ch11_06.htm"><IMG SRC="gifs/txtnexta.gif" ALT="Next" border=0></A></td>
</tr>
</table>

&nbsp;
<hr align=left width=515>
</DIV>
<DIV CLASS=sect1>
<h2 CLASS=sect1><A CLASS="TITLE" NAME="JNUT2-CH-11-SECT-5">11.5 Localizing User-Visible Messages</A></h2>

<P CLASS=para>
<A NAME="CH11.INTERNATIONA2"></A><A NAME="CH11.ERROR.MESSAG1"></A><A NAME="CH11.MESSAGES-INT1"></A>The third task of internationalization involves ensuring that
there are no user-visible strings that are hard-coded in an
application, instead of being looked up based on the
locale.  In <A HREF="ch11_04.htm#JNUT2-CH-11-EX-3">Example 11.3</A>,
for example, the strings "Portfolio value", "Symbol",
"Shares", and others are hard-coded in the
application and appear in English, even when the program
is run in the French locale.  The only way to prevent this is
to fetch all user-visible messages at runtime, and to
translate every message into each of the languages that your
application must support.

<P CLASS=para>
<A NAME="CH11.RESOURCEBUND1"></A>Java 1.1 helps you handle this task with the <tt CLASS=literal>ResourceBundle</tt>
class of the <tt CLASS=literal>java.util</tt> package.  This class
represents a "bundle" of resources that can be looked up
by name.  You define a localized resource bundle for each
locale you want to support, and Java takes care of loading
the correct bundle for the default (or specified)
locale.  With the correct bundle loaded, you can look up the
resources (typically strings) that your program needs at
runtime.

<DIV CLASS=sect2>
<h3 CLASS=sect2><A CLASS="TITLE" NAME="JNUT2-CH-11-SECT-5.1">Working with Resource Bundles</A></h3>

<P CLASS=para>
To define a bundle of localized resources, you create a
subclass of <tt CLASS=literal>ResourceBundle</tt> and provide definitions
for the <tt CLASS=literal>handleGetObject()</tt> and <tt CLASS=literal>getKeys()</tt>
methods.  <tt CLASS=literal>handleGetObject()</tt> is passed the name of a
resource; it should return an appropriate localized version
of that resource.  <tt CLASS=literal>getKeys()</tt> should return an
<tt CLASS=literal>Enumeration</tt> object that gives the user a
list of all resource names defined in the
<tt CLASS=literal>ResourceBundle</tt>.  Instead of subclassing
<tt CLASS=literal>ResourceBundle</tt> directly, however, it is often easier to
subclass <tt CLASS=literal>ListResourceBundle</tt>.  You can also
simply provide a property file (see the <tt CLASS=literal>java.util.Properties</tt>
class) that <tt CLASS=literal>ResourceBundle.getBundle()</tt> uses to
create an instance of <tt CLASS=literal>PropertyResourceBundle</tt>.

<P CLASS=para>
To use localized resources from a <tt CLASS=literal>ResourceBundle</tt> in
a program, you should first call the static
<tt CLASS=literal>getBundle()</tt> method, which dynamically loads and
instantiates a <tt CLASS=literal>ResourceBundle</tt>, as described below.
The returned <tt CLASS=literal>ResourceBundle</tt> has the name you specify
and is appropriate for the specified locale (or
for the default locale, if no locale is explicitly
specified).  Once you have obtained a <tt CLASS=literal>ResourceBundle</tt>
object with <tt CLASS=literal>getBundle()</tt>, you use the
<tt CLASS=literal>getObject()</tt> method to look up resources by name.
Note that there is a convenience method, <tt CLASS=literal>getString()</tt>,
that simply casts the value returned by <tt CLASS=literal>getObject()</tt> to
be a <tt CLASS=literal>String</tt> object.

<P CLASS=para>
When you call <tt CLASS=literal>getBundle()</tt>, you specify the base name
of the desired <tt CLASS=literal>ResourceBundle</tt> and a desired
locale (if you don't want to rely on the default locale). Recall that
a <tt CLASS=literal>Locale</tt> is specified with a two-letter language
code, an optional two-letter country code, and an optional
variant string.  <tt CLASS=literal>getBundle()</tt> looks for an
appropriate <tt CLASS=literal>ResourceBundle</tt> class for the locale by
appending this locale information to the base name for the
bundle.  The method looks for an appropriate class
with the following algorithm:

<P>
<OL CLASS=orderedlist>
<li CLASS=listitem>Search for a class with the following name:

<P CLASS=para>
<DIV CLASS=screen>
<P>
<PRE>
<I CLASS=emphasis><tt CLASS=literal>basename</tt></I>_<I CLASS=emphasis><tt CLASS=literal>language</tt></I>_<I CLASS=emphasis><tt CLASS=literal>country</tt></I>_<I CLASS=emphasis><tt CLASS=literal>variant</tt></I>
</PRE>
</DIV>

<P CLASS=para>
If no such class is found, or no variant string is specified
for the locale, it goes to the next step.

<P>
<li CLASS=listitem>Search for a class with the following name:

<P CLASS=para>
<DIV CLASS=screen>
<P>
<PRE>
<I CLASS=emphasis><tt CLASS=literal>basename</tt></I>_<I CLASS=emphasis><tt CLASS=literal>language</tt></I>_<I CLASS=emphasis><tt CLASS=literal>country</tt></I>
</PRE>
</DIV>

<P CLASS=para>
If no such class is found, or no country code is specified
for the locale, it goes to the next step.

<P>
<li CLASS=listitem>Search for a class with the following name:

<P CLASS=para>
<DIV CLASS=screen>
<P>
<PRE>
<I CLASS=emphasis><tt CLASS=literal>basename</tt></I>_<I CLASS=emphasis><tt CLASS=literal>language</tt></I>
</PRE>
</DIV>

<P CLASS=para>
If no such class is found, it goes to the final step.

<P>
<li CLASS=listitem>Search for a class which has the same name as the basename, or in
other words, seach for a class with the following name:

<P CLASS=para>
<DIV CLASS=screen>
<P>
<PRE>
<I CLASS=emphasis><tt CLASS=literal>basename</tt></I>
</PRE>
</DIV>

<P CLASS=para>
This represents a default resource bundle used by any locale
that is not explicitly supported.

<P>
</OL>
<P CLASS=para>
At each step in the above process, <tt CLASS=literal>getBundle()</tt>
checks first for a class file with the given name.  If no
class file is found, it uses the
<tt CLASS=literal>getResourceAsStream()</tt> method of <tt CLASS=literal>ClassLoader</tt>
to look for a <tt CLASS=literal>Properties</tt> file with the same name as
the class and a <I CLASS=emphasis>.properties</I> extension.  If such a
properties file is found, its contents are used to create a
<tt CLASS=literal>Properties</tt> object and <tt CLASS=literal>getBundle()</tt>
instantiates and returns a <tt CLASS=literal>PropertyResourceBundle</tt>
that exports the properties in the <tt CLASS=literal>Properties</tt> file
through the <tt CLASS=literal>ResourceBundle</tt> API.

<P CLASS=para>
If <tt CLASS=literal>getBundle()</tt> cannot find a class or properties
file for the specified locale in any of the four search
steps above, it repeats the search using the default locale
instead of the specified locale.  If no appropriate
<tt CLASS=literal>ResourceBundle</tt> is found in this search either,
<tt CLASS=literal>getBundle()</tt> throws a
<tt CLASS=literal>MissingResourceException</tt>.

<P CLASS=para>
Any <tt CLASS=literal>ResourceBundle</tt> object may have a parent
<tt CLASS=literal>ResourceBundle</tt> specified for it.  When you look up a
named resource in a <tt CLASS=literal>ResourceBundle</tt>,
<tt CLASS=literal>getObject()</tt> first looks in the specified bundle, but
if the named resource is not defined in that bundle, it
recursively looks in the parent bundle.  Thus, every
<tt CLASS=literal>ResourceBundle</tt> "inherits" the resources of its
parent, and may choose to "override" some, or all, of these
resources.  (Note that we are using the terms "inherit" and
"override" in a different sense than we do when talking
about classes that inherit and override methods in their
superclass.)  What this means is that every
<tt CLASS=literal>ResourceBundle</tt> you define does not have to
define every resource
required by your application.  For example, you might define
a <tt CLASS=literal>ResourceBundle</tt> of messages to display to
French-speaking users.  Then you might define a smaller and
more specialized <tt CLASS=literal>ResourceBundle</tt> that overrides a few
of these messages so that they are appropriate for
French-speaking users who live in Canada.

<P CLASS=para>
Your application is not required to find and set up the
parent objects for the <tt CLASS=literal>ResourceBundle</tt> objects it uses.  The
<tt CLASS=literal>getBundle()</tt> method actually does this for you.  When
<tt CLASS=literal>getBundle()</tt> finds an appropriate class or properties
file as described above, it does not immediately return the
<tt CLASS=literal>ResourceBundle</tt> it has found.  Instead, it continues
through the remaining steps in the search process listed
above, looking for less-specific class or properties files
from which the <tt CLASS=literal>ResourceBundle</tt> may "inherit"
resources.  If and when <tt CLASS=literal>getBundle()</tt> finds these
less-specific resource bundles, it sets them up as the
appropriate ancestors of the original
bundle.  Only once it has checked all possibilities does it
return the original <tt CLASS=literal>ResourceBundle</tt> object that it
created.

<P CLASS=para>
To continue the example begun above, when a program runs in
Qu&eacute;bec, <tt CLASS=literal>getBundle()</tt> might first find a small
specialized <tt CLASS=literal>ResourceBundle</tt> class that has only a few
specific Qu&eacute;becois resources.
Next, it looks for a more general <tt CLASS=literal>ResourceBundle</tt>
that contains French messages and it sets this bundle as the
parent of the original Qu&eacute;becois bundle.  Finally,
<tt CLASS=literal>getBundle()</tt> looks for (and probably finds) a class
that defines a default set of resources, probably written in
English (assuming that English is the native tongue of the
original programmer).  This default bundle is set as the
parent of the French bundle (which makes it the grandparent
of the Qu&eacute;becois bundle).  When the application looks up a
named resource, the Qu&eacute;becois bundle is searched first.  If
the resource is not defined there, then the French bundle is
searched, and finally, any named resource not found in the
French bundle is looked up in the default bundle.

</DIV>

<DIV CLASS=sect2>
<h3 CLASS=sect2><A CLASS="TITLE" NAME="JNUT2-CH-11-SECT-5.2">ResourceBundle Example</A></h3>

<P CLASS=para>
Examining some code makes this discussion of resource
bundles much clearer. <A HREF="ch11_05.htm#JNUT2-CH-11-EX-4">Example 11.4</A>
is a convenience routine for creating menu panes.  Given a
list of menu item names, it looks up labels and menu
shortcuts for those named menu items in a resource bundle and
creates a localized menu pane.  The example has a simple
test program attached. <A HREF="ch11_05.htm#JNUT2-CH-11-FIG-2">Figure 11.2</A>
shows the menus it creates in the American, British, and
French locales.  This program could not run, of course,
without localized resource bundles from which the localized
menu labels are looked up. <A HREF="ch11_05.htm#JNUT2-CH-11-EX-4">Example 11.4</A>
shows American, British, and French resource files used to
create each of the menus shown in the figure.

<DIV CLASS=figure>
<h4 CLASS=figure><A CLASS="TITLE" NAME="JNUT2-CH-11-FIG-2">Figure 11.2: Localized menu panes</A></h4>


<p>
<img align=middle src="./figs/jn2_1102.gif" alt="[Graphic: Figure 11-2]" width=464 height=273 border=0>

</DIV>

<DIV CLASS=example>
<h4 CLASS=example><A CLASS="TITLE" NAME="JNUT2-CH-11-EX-4">Example 11.4: Creating Localized Menu Panes</A></h4>

<DIV CLASS=screen>
<P>
<PRE>
import java.awt.*;
import java.awt.event.*;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.MissingResourceException;
/** A convenience class to automatically create localized menu panes. */
public class SimpleMenu {
  /** The convenience method that creates menu panes. */
  public static Menu create(String bundlename,
                            String menuname, String[] itemnames,
                            ActionListener listener, boolean popup) {
    // Get the resource bundle used for this menu.
    ResourceBundle b = ResourceBundle.getBundle(bundlename);
    // Get the menu title from the bundle.  Use name as default label.
    String menulabel;
    try { menulabel = b.getString(menuname + ".label"); }
    catch(MissingResourceException e) { menulabel = menuname; }
    // Create the menu pane.
    Menu m;
    if (popup) m = new PopupMenu(menulabel);
    else m = new Menu(menulabel);
    // For each named item in the menu.
    for(int i = 0; i &lt; itemnames.length; i++) {
      // Look up the label for the item, using name as default.
      String itemlabel;
      try { itemlabel=b.getString(menuname + "." + itemnames[i] + ".label"); }
      catch (MissingResourceException e) { itemlabel = itemnames[i]; }
      // Look up a shortcut for the item, and create the menu shortcut, if any.
      String shortcut;
      try{shortcut = b.getString(menuname + "." + itemnames[i]+".shortcut"); }
      catch (MissingResourceException e) { shortcut = null; }
      MenuShortcut ms = null;
      if (shortcut != null) ms = new MenuShortcut(shortcut.charAt(0));
      // Create the menu item.
      MenuItem mi;
      if (ms != null) mi = new MenuItem(itemlabel, ms);
      else mi = new MenuItem(itemlabel);
      // Register an action listener and command for the item.
      if (listener != null) {
        mi.addActionListener(listener);
        mi.setActionCommand(itemnames[i]);
      }
      // Add the item to the menu.
      m.add(mi);
    }
    // Return the automatically created localized menu.
    return m;
  }
  /** A simple test program for the above code. */
  public static void main(String[] args) {
    // Set the default locale based on the command-line args.
    if (args.length == 2) Locale.setDefault(new Locale(args[0], args[1]));
    Frame f = new Frame("SimpleMenu Test");  // Create a window.
    MenuBar menubar = new MenuBar();         // Create a menubar.
    f.setMenuBar(menubar);                   // Add menubar to window.
    // Create a menu using our convenience routine (and the default locale).
    Menu colors = SimpleMenu.create("Menus", "colors",
                                    new String[] { "red", "green", "blue" },
                                    null, false);
    menubar.add(colors);                     // Add the menu to the menubar.
    f.setSize(300, 150);                     // Set the window size.
    f.show();                                // Pop the window up.
  }
}
</PRE>
</DIV>

</DIV>

<P CLASS=para>
This example does not stand alone.  It relies on resource
bundles to localize the menu. <A HREF="ch11_05.htm#JNUT2-CH-11-EX-5">Example 11.5</A>
shows three property files that serve as resource bundles for
this example.  Note that this single example listing
actually contains the bodies of three separate files.

<DIV CLASS=example>
<h4 CLASS=example><A CLASS="TITLE" NAME="JNUT2-CH-11-EX-5">Example 11.5: Property Files as Resource Bundles</A></h4>

<DIV CLASS=screen>
<P>
<PRE>
# The file Menus.properties is the default "Menus" resource bundle.
# As an American programmer, I made my own locale the default.
colors.label=Colors
colors.red.label=Red
colors.red.shortcut=R
colors.green.label=Green
colors.green.shortcut=G
colors.blue.label=Blue
colors.blue.shortcut=B
# This is the file Menus_en_GB.properties.  It is the resource bundle for
# British English.  Note that it overrides only a single resource definition
# and simply inherits the rest from the default (American) bundle.
colors.label=Colours
# This is the file Menus_fr.properties.  It is the resource bundle for all
# French-speaking locales.  It overrides most, but not all, of the resources
# in the default bundle.
colors.label=Couleurs
colors.red.label=Rouge
colors.green.label=Vert
colors.green.shortcut=V
colors.blue.label=Bleu
</PRE>
</DIV>

</DIV>

</DIV>

</DIV>


<DIV CLASS=htmlnav>

<P>
<HR align=left width=515>
<table width=515 border=0 cellpadding=0 cellspacing=0>
<tr>
<td width=172 align=left valign=top><A HREF="ch11_04.htm"><IMG SRC="gifs/txtpreva.gif" ALT="Previous" border=0></A></td>
<td width=171 align=center valign=top><a href="index.htm"><img src='gifs/txthome.gif' border=0 alt='Home'></a></td>
<td width=172 align=right valign=top><A HREF="ch11_06.htm"><IMG SRC="gifs/txtnexta.gif" ALT="Next" border=0></A></td>
</tr>
<tr>
<td width=172 align=left valign=top>Handling Local Customs</td>
<td width=171 align=center valign=top><a href="index/idx_0.htm"><img src='gifs/index.gif' alt='Book Index' border=0></a></td>
<td width=172 align=right valign=top>Formatted Messages</td>
</tr>
</table>
<hr align=left width=515>

<IMG SRC="gifs/smnavbar.gif" USEMAP="#map" BORDER=0> 
<MAP NAME="map"> 
<AREA SHAPE=RECT COORDS="0,0,108,15" HREF="../javanut/index.htm"
alt="Java in a Nutshell"> 
<AREA SHAPE=RECT COORDS="109,0,200,15" HREF="../langref/index.htm" 
alt="Java Language Reference"> 
<AREA SHAPE=RECT COORDS="203,0,290,15" HREF="../awt/index.htm" 
alt="Java AWT"> 
<AREA SHAPE=RECT COORDS="291,0,419,15" HREF="../fclass/index.htm" 
alt="Java Fundamental Classes"> 
<AREA SHAPE=RECT COORDS="421,0,514,15" HREF="../exp/index.htm" 
alt="Exploring Java"> 
</MAP>
</DIV>

</BODY>
</HTML>
